package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.utils.UserContext;
import cn.hutool.core.bean.BeanUtil;
import com.tianji.learning.domain.dto.AddNotesDto;
import com.tianji.learning.domain.po.Note;
import com.tianji.learning.domain.po.UserNote;
import com.tianji.learning.mapper.NoteMapper;
import com.tianji.learning.service.INoteService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.service.IUserNoteService;
import org.checkerframework.checker.units.qual.N;
import org.springframework.beans.factory.annotation.Autowired;
import com.tianji.learning.domain.dto.UpdateNoteDto;
import com.tianji.learning.service.IUserNoteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.learning.domain.dto.NotePageDto;
import com.tianji.learning.domain.query.NotePageQuery;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.dto.course.CourseSearchDTO;
import com.tianji.learning.domain.vo.NoteDetailVo;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.query.UserNoteQuery;
import com.tianji.learning.domain.vo.UserNotePageVo;
import com.tianji.learning.service.ILearningLessonService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.HashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 课程笔记表 服务实现类
 * </p>
 *
 * @author lvbin
 * @author 张政
 * @author L.HS
 */
@Slf4j
@Service
public class NoteServiceImpl extends ServiceImpl<NoteMapper, Note> implements INoteService {


    @Autowired
    private IUserNoteService userNoteService;
    @Autowired
    CourseClient courseClient;
    @Autowired
    CatalogueClient catalogueClient;
    @Autowired
    UserClient userClient;
    @Autowired
    private CategoryCache categoryCache;

    /**
     * 3.1.2.采集、取消采集笔记
     * @param  id 要采集的笔记id
     * @author lvbin
     */
    @Override
    public void gatherNotes(Long id) {
        //采集笔记就是将他人笔记复制到我的笔记中且做私密处理。因此请求时只需要知道要采集的笔记id即可。与之对应的还有一个取消采集。
        //当前用户id
        Long userid = UserContext.getUser();
        //用户所采集的笔记的实体
        Note noteDb = this.getById(id);
        noteDb.setGatherTimes(noteDb.getGatherTimes()+1);
        noteDb.setUpdateTime(LocalDateTime.now());

        UserNote userNote = new UserNote();
        userNote.setNoteId(id);
        userNote.setUserId(userid);

        this.updateById(noteDb);
        userNoteService.save(userNote);
    }

    @Override
    public void deleteNotes(Long id) {
        //删除笔记
        Long userid = UserContext.getUser();

        LambdaQueryWrapper<UserNote> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserNote::getNoteId, id)
                .eq(UserNote::getUserId, userid);

        userNoteService.remove(queryWrapper);
        Note noteDb = this.getById(id);
        noteDb.setGatherTimes(noteDb.getGatherTimes() -1);
        this.updateById(noteDb);

    }
    @Override
    public void editNote(Long id, UpdateNoteDto dto) {
        Note note = BeanUtil.copyProperties(dto, Note.class);
        this.lambdaUpdate().set(dto.getContent() != null, Note::getContent, note.getContent())
                .set(dto.getIsPrivate() != null, Note::getIsPrivate, note.getIsPrivate())
                .set(Note::getUpdateTime, LocalDateTime.now())
                .eq(Note::getId, id)
                .update();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteNote(Long id) {
        LambdaQueryWrapper<UserNote> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserNote::getNoteId, id);
        LambdaQueryWrapper<Note> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Note::getId, id);
        this.remove(queryWrapper);
        userNoteService.remove(lambdaQueryWrapper);
    }

    @Override
    public void addNotes(AddNotesDto dto) {
        //恭喜你你看到了lhs的代码
        Long userId = UserContext.getUser();
        //新增笔记到note
        Note note = new Note();
        BeanUtil.copyProperties(dto, note);
        note.setUsrId(userId);
        this.save(note);
    }


    @Override
    public PageDTO<NotePageDto> selectNotePage(NotePageQuery query) {
        List<Long> courseIds = null;
        Page<Note> page = query.toMpPage("update_time", false);
        LambdaQueryWrapper<Note> wrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(query.getName())){
            courseIds = courseClient.getIdByBlurName(query.getName());
        }
        if (CollUtils.isNotEmpty(courseIds)){
            wrapper.in(Note::getCourseId, courseIds);
        }
        wrapper.eq(query.getHidden() != null, Note::getHidden, query.getHidden());
        wrapper.ge(query.getBeginTime()!=null, Note::getUpdateTime,query.getBeginTime());
        wrapper.le(query.getEndTime() != null, Note::getUpdateTime, query.getEndTime());
        this.page(page,wrapper);
        List<Note> noteList = page.getRecords();
        if (CollUtils.isEmpty(noteList)){
            return PageDTO.empty(page);
        }
        // 查询到的课程id
        List<Long> courseIdsPage = noteList.stream().map(Note::getCourseId).collect(Collectors.toList());
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIdsPage);
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = courseInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        List<Long> chapterIds = noteList.stream().map(Note::getChapterId).collect(Collectors.toList());
        Map<Long, CataSimpleInfoDTO> chapterMap = getSectionOrChapter(chapterIds);
        List<Long> sectionIds = noteList.stream().map(Note::getSectionId).collect(Collectors.toList());
        Map<Long, CataSimpleInfoDTO> sectionMap = getSectionOrChapter(sectionIds);
        List<Long> userIds = noteList.stream().map(Note::getUsrId).collect(Collectors.toList());
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);
        Map<Long, UserDTO> userMap = userDTOS.stream().collect(Collectors.toMap(UserDTO::getId, u -> u));
        List<NotePageDto> result = new ArrayList<>();
        for (Note note : noteList) {
            NotePageDto notePageDto = BeanUtil.copyProperties(note,NotePageDto.class);
            notePageDto.setAuthorName(userMap.get(note.getUsrId()).getName());
            notePageDto.setCourseName(courseInfoMap.get(note.getCourseId()).getName()==null? "" : courseInfoMap.get(note.getCourseId()).getName());
            notePageDto.setChapterName(chapterMap.get(note.getChapterId()).getName()==null? "" : chapterMap.get(note.getChapterId()).getName());
            notePageDto.setSectionName(sectionMap.get(note.getSectionId()).getName()==null?"" : sectionMap.get(note.getSectionId()).getName());
            notePageDto.setUsedTimes(note.getGatherTimes());
            notePageDto.setAuthorName(userMap.get(note.getUsrId()).getName()==null? "" : userMap.get(note.getUsrId()).getName());
            result.add(notePageDto);
        }
        return PageDTO.of(page,result);
    }

    private Map<Long, CataSimpleInfoDTO> getSectionOrChapter(List<Long> ids){
        List<CataSimpleInfoDTO> chapterList = catalogueClient.batchQueryCatalogue(ids);
        Map<Long, CataSimpleInfoDTO> collect = chapterList.stream().collect(Collectors.toMap(CataSimpleInfoDTO::getId, c -> c));
        return collect;
    }


    @Override
    public NoteDetailVo getNoteDetailById(Long id) {
        Note note = this.getById(id);

        NoteDetailVo noteDetailVo = BeanUtil.copyProperties(note, NoteDetailVo.class);
        noteDetailVo.setUsedTimes(note.getGatherTimes());


        Long courseId = note.getCourseId();
        Long chapterId = note.getChapterId();
        Long sectionId = note.getSectionId();
        CourseSearchDTO courseDto = courseClient.getSearchInfo(courseId);
        //课程名称
        String courseName = courseDto.getName();

        List<CataSimpleInfoDTO> chapters = catalogueClient.batchQueryCatalogue(Arrays.asList(chapterId,sectionId));
        Map<Long, CataSimpleInfoDTO> chapterMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(chapters)) {
            chapterMap = chapters.stream().collect(Collectors.toMap(CataSimpleInfoDTO::getId, Function.identity()));
        }

        //章 和 节

        CataSimpleInfoDTO sectionDto = chapterMap.get(sectionId);
        CataSimpleInfoDTO chapterDto = chapterMap.get(chapterId);
        //多级分类
        String nameByLv3Id = categoryCache.getNameByLv3Id(courseDto.getCategoryIdLv3());

        Long usrId = note.getUsrId();
        UserDTO userDTO = userClient.queryUserById(usrId);

        /**引用者名称*/
        LambdaQueryWrapper<UserNote> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserNote::getNoteId, id);
        List<UserNote> list = userNoteService.list(queryWrapper);
        List<Long> userIds = list.stream().map(UserNote::getUserId).collect(Collectors.toList());
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);
        List<String> nameList = userDTOS.stream().map(UserDTO::getName).collect(Collectors.toList());


        noteDetailVo.setCourseName(courseName == null ? "" : courseName);
        noteDetailVo.setChapterName(chapterDto.getName() == null ? "" : chapterDto.getName());
        noteDetailVo.setSectionName(sectionDto.getName() == null ? "" : sectionDto.getName());
        noteDetailVo.setCategoryNames(nameByLv3Id == null ? "" : nameByLv3Id);
        noteDetailVo.setAuthorName(userDTO.getName());
        noteDetailVo.setAuthorPhone(userDTO.getCellPhone());
        noteDetailVo.setGathers(nameList);
        return noteDetailVo;

    }

    @Autowired
    private ILearningLessonService lessonService;

    @Override
    public PageDTO<UserNotePageVo> pageQuery(UserNoteQuery query) {
        Long userId = UserContext.getUser();

        List<LearningLesson> learningLessons = lessonService.lambdaQuery().eq(query.getCousrseId() != null, LearningLesson::getCourseId, query.getCousrseId())
                .eq(LearningLesson::getUserId, userId).list();
        if(CollectionUtils.isEmpty(learningLessons)){
            throw new BizIllegalException("只有购买课程的用户才可查看笔记");
        }

        List<UserNote> userNotes = userNoteService.lambdaQuery().eq(UserNote::getUserId, userId).list();
        List<Long> userNoteIds = null;
        Map<Long, UserNote> userNoteMap = null;
        if(!CollectionUtils.isEmpty(userNotes)){
            userNoteIds = userNotes.stream().map(UserNote::getNoteId).collect(Collectors.toList());
            userNoteMap = userNotes.stream().collect(Collectors.toMap(UserNote::getNoteId, Function.identity()));
        }

        Page<Note> page = query.toMpPageDefaultSortByCreateTimeDesc();
        LambdaQueryWrapper<Note> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(query.getCousrseId()!= null,Note::getCourseId,query.getCousrseId());
        queryWrapper.eq(query.getSectionId()!=null,Note::getSectionId,query.getSectionId());
        queryWrapper.eq(Note::getHidden,false);

        if(query.getOnlyMine()){
            if(CollectionUtils.isEmpty(userNoteIds)){
                queryWrapper.and(wrapper -> {
                    wrapper.eq(Note::getUsrId,userId);
                });
            }else {
                List<Long> finalUserNoteIds = userNoteIds;
                queryWrapper.and(wrapper -> {
                    wrapper.eq(Note::getUsrId,userId).or().in(Note::getId, finalUserNoteIds);
                });
            }
        }else {
            queryWrapper.eq(Note::getIsPrivate,false);
        }

        this.page(page,queryWrapper);

        List<Note> records = page.getRecords();
        if(CollectionUtils.isEmpty(records)){
            return PageDTO.empty(page);
        }

        List<Long> userIds = records.stream().map(Note::getUsrId).collect(Collectors.toList());
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);
        if(CollectionUtils.isEmpty(userDTOS)){
            log.error("根据userIds:{}查询不到用户",userIds);
            throw new BizIllegalException("网络异常，请稍后重试");
        }
        Map<Long, UserDTO> userDTOMap = userDTOS.stream().collect(Collectors.toMap(UserDTO::getId, Function.identity()));

        List<UserNotePageVo> pageVos = new ArrayList<>();
        for (Note note : records) {
            UserNotePageVo vo = BeanUtil.copyProperties(note,UserNotePageVo.class);
            vo.setAuthorId(note.getUsrId());
            UserDTO userDTO = userDTOMap.get(note.getUsrId());
            if(userDTO == null){
                log.error("根据userId:{}查询不到用户",note.getUsrId());
                throw new BizIllegalException("网络异常，请稍后重试");
            }
            vo.setAuthorIcon(userDTO.getIcon());
            vo.setAuthorName(userDTO.getName());

            if(!CollectionUtils.isEmpty(userNoteMap)){
                UserNote userNote = userNoteMap.get(note.getId());
                if(userNote == null){
                    vo.setIsGathered(false);
                }else {
                    vo.setIsGathered(true);
                }
            }else {
                vo.setIsGathered(false);
            }

            pageVos.add(vo);
        }

        return PageDTO.of(page,pageVos);
    }

}
